////////////////////////////////////////////////////////////////////////////////
//File:					UglyTexture.cpp
//Author:				Kesitem
//This file is part of UglyProject.
//See UglyProject.txt for copyright informations.
////////////////////////////////////////////////////////////////////////////////
#include "UglyTexture.h"
#include "UglyLog.h"
#include "UglyOpenGlError.h"

namespace Ugly
{

/**
 * \brief Constructor.
 */
Texture::Texture()
{
    mId = 0;
}


/**
 * \brief Destructor.
 */
Texture::~Texture()
{
    destroy();
}


/**
 * \brief Create texture from file.
 *
 * \param   _Filename   File name.
 * \return False, if error.
 */
bool Texture::createFromFile(const std::string &_Filename)
{
    gLog << "Create texture: " << _Filename << "\n";

    //Save filename
    mFilename = _Filename;

    //Load image to SDL surface
    SDL_Surface *originalSurface = IMG_Load(mFilename.c_str());
    if(originalSurface == 0)
    {
        gLog << "Error! Texture::createFromFile: failed to load image from file\n";
        gLog << "   SDL error: " << SDL_GetError() << "\n";
        return false;
    }

    //Create a swapped surface
    SDL_Surface *surface = this->swapTexture(originalSurface);

    //Free original texture
    SDL_FreeSurface(originalSurface);

    //Get surface format
    GLenum internalFormat;
    GLenum format;
    if(surface->format->BytesPerPixel == 3)
    {
        internalFormat = GL_RGB;

        if(surface->format->Rmask == 0xff)
            format = GL_RGB;
        else
            format = GL_BGR;
    }
    else if(surface->format->BytesPerPixel == 4)
    {
        internalFormat = GL_RGBA;

        if(surface->format->Rmask == 0xff)
            format = GL_RGBA;
        else
            format = GL_BGRA;
    }
    else
    {
        gLog << "Error! Texture::createFromFile: invalid pixel format\n";
        SDL_FreeSurface(surface);
        return false;
    }

    //Generate texture object
    glGenTextures(1, &mId);
    if(gOpenGlError.check())
    {
        gLog << "Error! Texture::createFromFile: failed to create texture object\n";
        gLog << "   OpenGL error: " << gOpenGlError.getErrorDesc() << "\n";
        SDL_FreeSurface(surface);
        return false;
    }

    //Bind texture object
    glBindTexture(GL_TEXTURE_2D, mId);
    if(gOpenGlError.check())
    {
        gLog << "Error! Texture::createFromFile: failed to bind texture object\n";
        gLog << "   OpenGL error: " << gOpenGlError.getErrorDesc() << "\n";
        SDL_FreeSurface(surface);
        return false;
    }

    //Set texture data
    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, surface->w, surface->h, 0, format, GL_UNSIGNED_BYTE, surface->pixels);

    //Set filter
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    //Unbind texture
    glBindTexture(GL_TEXTURE_2D, 0);

    //Free SDL surface
    SDL_FreeSurface(surface);
    return true;
}


/**
 * \brief Destroy texture.
 */
void Texture::destroy()
{
    //Destroy texture
    if(mId != 0)
    {
        gLog << "Destroy texture: " << mFilename << "\n";
        glDeleteTextures(1, &mId);
        mId = 0;
    }

    //Clear filename
    mFilename.clear();
}


/**
 * \brief bind the texture.
 */
void Texture::bind()
{
    glBindTexture(GL_TEXTURE_2D, mId);
}


/**
 * \brief Unbind the texture.
 */
void Texture::unbind()
{
    glBindTexture(GL_TEXTURE_2D, 0);
}


/**
 * \brief Swap a SDL surface to match usual texture.
 *
 * \param   sourceSurface  Surface to swap.
 * \return Swaped surface.
 */
SDL_Surface *Texture::swapTexture(SDL_Surface *sourceSurface) const
{
    SDL_Surface *newSurface = SDL_CreateRGBSurface(0,
                                                   sourceSurface->w,
                                                   sourceSurface->h,
                                                   sourceSurface->format->BitsPerPixel,
                                                   sourceSurface->format->Rmask,
                                                   sourceSurface->format->Gmask,
                                                   sourceSurface->format->Bmask,
                                                   sourceSurface->format->Amask);


    SDL_LockSurface(sourceSurface);
    SDL_LockSurface(newSurface);

    int pitch = sourceSurface->pitch;
    for(int current_line = 0; current_line < sourceSurface->h; current_line ++)
    {
        memcpy(&((unsigned char* )newSurface->pixels)[current_line*pitch],
               &((unsigned char* )sourceSurface->pixels)[(sourceSurface->h - 1  - current_line)*pitch],
               pitch);
    }

    SDL_UnlockSurface(newSurface);
    SDL_UnlockSurface(sourceSurface);

    return newSurface;
}

}//namespace Ugly
